#include <stdio.h>      /* for printf() and fprintf() */
#include <sys/socket.h> /* for socket() and bind() */
#include <arpa/inet.h>  /* for sockaddr_in and inet_ntoa() */
#include <stdlib.h>     /* for atoi() and exit() */
#include <string.h>     /* for memset() */
#include <unistd.h>     /* for close() */
#include "str.h"


#define ECHOMAX 255     /* Longest string to echo */

void DieWithError(const char *errorMessage) /* External error handling function */
{
    perror(errorMessage);
    exit(1);
}

struct client {                         //Client Struct for incoming requests.
            char processor[16];
            int incar;
            int clientNum;
	    int reqNum;
            char response[5];
    };

struct client table[40]; // Array of client Structs for Client Table.

int main(int argc, char *argv[])
{
    int sock;                        /* Socket */
    struct sockaddr_in echoServAddr; /* Local address */
    struct sockaddr_in echoClntAddr; /* Client address */
    unsigned int cliAddrLen;         /* Length of incoming message */
    char echoBuffer[ECHOMAX];        /* Buffer for echo string */
    unsigned short echoServPort;     /* Server port */
    int recvMsgSize;                 /* Size of received message */
    char message[6]= ""; //store the message to be sent back to the client.
    char temp[6]= ""; //temporary array for manipulating message array.
    char ch;
    int r = -1; //old request number
    int R; //new request number
    int i;
    int j;
    int k;
    int count = 0; //counter for incrementing the Client Table.
    int new = 1; //To see if incoming request is new. 1 is New. 0 is old.
    int same = 0; //variable to keep track of the location in the Client Table where a stored response for a request is.     
    int pEqualsC[16]; //array to check if passed in client ip equals a processor number already in the Client Table.
    int flag =0; //Flag to see if received client ip is new or not. 1 is old. 0 is new.
    int tem; //temporary variable used to check if client ip is the same as stored processor number.

    struct request arequest;



    if (argc != 2)         /* Test for correct number of parameters */
    {
        fprintf(stderr,"Usage:  %s <UDP SERVER PORT>\n", argv[0]);
        exit(1);
    }

    echoServPort = atoi(argv[1]);  /* First arg:  local port */

    /* Create socket for sending/receiving datagrams */
    if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
        DieWithError("socket() failed");

    /* Construct local address structure */
    memset(&echoServAddr, 0, sizeof(echoServAddr));   /* Zero out structure */
    echoServAddr.sin_family = AF_INET;                /* Internet address family */
    echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
    echoServAddr.sin_port = htons(echoServPort);      /* Local port */

    /* Bind to the local address */
    if (bind(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0)
        DieWithError("bind() failed");
  
    for (;;) /* Run forever */
    {
        /* Set the size of the in-out parameter (For connecting the socket) */
        cliAddrLen = sizeof(echoClntAddr);

        /* Block until receive message from a client */
        if ((recvMsgSize = recvfrom(sock, (struct request *) &arequest, sizeof(arequest), 0,
            (struct sockaddr *) &echoClntAddr, &cliAddrLen)) < 0)
            DieWithError("recvfrom() failed");
	
	printf("-------------------------------------\n");
	printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr));
       
	printf("clientIP:%s\n", arequest.client_ip);
        printf("inc:%d\n", arequest.inc);
        printf("clientNum:%d\n", arequest.client);
        printf("request:%d\n", arequest.req);
        printf("char:%c\n", arequest.c);

	/*Random Number Generator */	
 	srand ( time(NULL));
        int randnum = rand() % (10-1+1)+1;
        printf("randnum:%d\n", randnum);
        
	if (arequest.client != table[count-1].clientNum) // reset old request number upon reception of new client number.
	{
		r = -1;
	}	

	R = arequest.req;
	printf("R:%d\n", R);
	printf("r before:%d\n", r);	

	if (randnum == 1) //Failure 1: Drop the Request
        {
                printf("request dropped\n");
        }
        else if (randnum == 2) // Failure 2: Service then drop the request.
        {
                printf("request serviced then dropped\n");
		
		for (i=0;i<=count;i++) // check to see if received client ip is already in the Client Table. If so, flag is set to 1.
                {
			for(j=0;j<16;j++)
			{
				if (table[i].processor[j] == arequest.client_ip[j])
					pEqualsC[j] = 1;
			}
				for(k=0;k<16;k++) 
				{
					tem = (pEqualsC[k] && pEqualsC[k+1]);
					pEqualsC[k+1] = tem;  
				}
				if(pEqualsC[16] == 1)
                                	flag =1;
		}
		
		printf("flag:%d\n", flag);
		
	for (i=0;i<=count;i++) //check to see if a request with Processor#, Incarnation#, Client#, and Request# already exists in the Client Table.If so, new is set to 0 and flag is reset to 0.
	{

		if((flag == 1) && (table[i].incar <= arequest.inc) && (table[i].clientNum == arequest.client) && (table[i].reqNum == arequest.req))
			{
				new = 0;
				flag = 0;		
			} 
	}
		
		printf("new:%d\n", new);
		if (new == 1) // if request is new make a new entry in the Client Table.
		{
			for(i=0;i<16;i++)
                        {
                                table[count].processor[i] = arequest.client_ip[i];
                        }
                	table[count].incar = arequest.inc;
                	table[count].clientNum = arequest.client;
                	table[count].reqNum = arequest.req;
			new = 1;		
		}		

		if (R<r) //ignore request
                {
                        printf("R<r\n");
                }

                else if (R==r && new ==0) // request already serviced, do nothing.
                {
                        printf("R=r\n");

                }
                else if ((R>r) || (R==r && new ==1)) //service and record request. Send response
                {
                        message[0] = arequest.c;

                        for (i=1;i<5;i++)
                        {
                                message[i] = temp[i-1];
                        }

                        for(i=0;i<5;i++)
                        {
                                temp[i] = message[i];
                        }

                        printf("message:%s\n", message);
                        //printf("temp:%s\n", temp);

			for(i=0;i<5;i++)
			{
       				table[count].response[i] = message[i];
			}
			count++;
        	}
		new =1;
	}
        else if (randnum >2 && randnum <11) //Success: service request and update client table.
        {
		printf("Request Serviced and Message Sent\n");

		for (i=0;i<=count;i++) // check to see if received client ip is already in the Client Table.
                {
                        for(j=0;j<16;j++)
                        {
                                if (table[i].processor[j] == arequest.client_ip[j])
                                        pEqualsC[j] = 1;
                        }
                                for(k=0;k<16;k++)
                                {
                                        tem = (pEqualsC[k] && pEqualsC[k+1]);
					pEqualsC[k+1] = tem;
                                }
                                if(pEqualsC[16] == 1)
                                        flag =1;
                 }
         
		printf("flag:%d\n", flag);

		for (i=0;i<=count;i++) // search client table for matches
                {
                        if((flag == 1) && (table[i].incar <= arequest.inc) && (table[i].clientNum == arequest.client) && (table[i].reqNum == arequest.req))
                        {
                                new = 0;
				flag = 0;
				if (new == 0)
				{
					same = i;
				}
                        }
                }
		
		printf("new:%d\n", new);
                if (new == 1) //no match, write to the client table
                {
                        for(i=0;i<16;i++)
                        {
                                table[count].processor[i] = arequest.client_ip[i];
                        }
                        table[count].incar = arequest.inc;
                        table[count].clientNum = arequest.client;
                        table[count].reqNum = arequest.req;
                        new = 1;
                }
	
        	if (R<r) //ignore request
		{
			printf("R<r\n");
		}

		else if (R==r && new == 0) // send stored response
		{
			printf("R=r\n");
			for (i=0;i<5;i++)
			{
				message[i] = table[same].response[i];
			}
			new = 1;
			same = 0;
			printf("message:%s\n", message);
			
			if (sendto(sock, &message, recvMsgSize, 0,
                (struct sockaddr *) &echoClntAddr, sizeof(echoClntAddr)) != recvMsgSize)
                        DieWithError("sendto() sent a different number of bytes than expected");

		}
		else if ((R>r) || (R==r && new ==1)) //service and record request. Send response.
		{    
			message[0] = arequest.c;

			for (i=1;i<5;i++)
			{
				message[i] = temp[i-1];
			}

			for(i=0;i<5;i++)
			{
				temp[i] = message[i];
			}
	
			printf("message:%s\n", message);
			//printf("temp:%s\n", temp);
		
			for(i=0;i<5;i++)
                        {
                                table[count].response[i] = message[i];
                        }
			count++;
	       /* Send received datagram back to the client */
     	 		 if (sendto(sock, &message, recvMsgSize, 0, 
                (struct sockaddr *) &echoClntAddr, sizeof(echoClntAddr)) != recvMsgSize)
            		DieWithError("sendto() sent a different number of bytes than expected");
		}
		
	}
	r = R;
	new = 1;   
	printf("r after:%d\n\n", r);//List the client table
		for(i=0;i<count;i++)
                        {
                               printf("table processor:%s\n", table[i].processor);
			       printf("table inc:%d\n", table[i].incar);
                               printf("table clientNum:%d\n", table[i].clientNum); 
                               printf("table reqNum:%d\n", table[i].reqNum);
			       printf("table response:%s\n\n", table[i].response);
                        }
	
    }
    /* NOT REACHED */
}
